import Random = ps.Random;
import Print = ps.Print;
import Mathf = ps.Mathf;
import Tween = ps.Tween;

/** 游戏主对象 */
let main: MainBeh;

enum GameEvent {
  a,
  b,
  c,
}

class MainBeh extends ps.Behaviour {
  enemyMan: qc.Node;
  enemyHorse: qc.Node;
  teamMan: qc.Node;
  teamHorse: qc.Node;

  manBtn: qc.Node;
  horseBtn: qc.Node;
  fightBtn: qc.Node;
  gameEvent: ps.EventDispatcher = new ps.EventDispatcher();
  /** 序列化 */
  private serializableFields: Object = {
    enemyMan: qc.Serializer.NODE,
    enemyHorse: qc.Serializer.NODE,
    teamMan: qc.Serializer.NODE,
    teamHorse: qc.Serializer.NODE,

    manBtn: qc.Serializer.NODE,
    horseBtn: qc.Serializer.NODE,
    fightBtn: qc.Serializer.NODE,
  };
  constructor(gameObject: qc.Node) {
    super(gameObject);
    main = this;
  }

  enemies: qc.Node[] = [];

  teamArea: qc.Node;
  enemyArea: qc.Node;
  dancePoint: qc.Node;
  foodProgress: qc.ProgressBar;
  progressWei: qc.ProgressBar;
  progressWu: qc.ProgressBar;

  foodText: qc.UIText;
  foodCount: number = 4000;
  attackMinDistance: number = 50;
  bowCount: qc.UIText;
  horseCount: qc.UIText;
  bowTotal: number;
  horseTotal: number;
  /** 试玩初始化的处理 */
  onInit() {
    ps.Print.purple("mainInit");
    //在这里初始化游戏场景需要的东西
    // ps.GameConfig.createTemplete();

    this.teamArea = UIRoot.getChild("teamArea");
    this.enemyArea = UIRoot.getChild("enemyArea");
    this.dancePoint = UIRoot.getChild("dancePoint");
    this.bowCount = UIRoot.getChild("bowCount") as qc.UIText;
    this.horseCount = UIRoot.getChild("horseCount") as qc.UIText;
    this.progressWei = UIRoot.getChild("progressWei") as qc.ProgressBar;
    this.progressWu = UIRoot.getChild("progressWu") as qc.ProgressBar;
    this.dancePoint.children.forEach((group) => {
      this.adjustIndex(group.children);
    });
    this.foodProgress = UIRoot.getChild("foodProgress") as qc.ProgressBar;

    this.foodText = UIRoot.getChild("foodText") as qc.UIText;

    this.loopDance();
    this.finger = UIRoot.getChild("hand").getScript(ps.Finger);

    this.guide();

    this.initData();
  }
  /** 试玩开始时的处理 */
  onStart() {
    ps.Print.purple("mainStart");
    //动态参数的使用,playAgain参数会随着再玩一次而减少
    // console.log(GAME_CFG.playAgain);

    this.listenButton();

    this.teamArea.interactive = true;
    this.teamArea.onDrag.add((target, e) => {
      if (!this.currentTeamRole) return;
      this.drawStarted = true;
      this.game.timer.remove(this.guidingDrawChangeLoop);
      this.guidingDraw = false;
      this.onDraw(new qc.Point(e.source.x, e.source.y));
    });

    ps.Audio.changeBg("bm_bgm");

    console.log(
      "%c Author By: https://github.com/JASONPANGGO",
      "color:#ff0;background-color:#000"
    );
  }
  /** 试玩结束时的处理 */
  onEnd() {
    ps.Print.purple("mainEnd");
  }
  /** 再来一次时的处理(onInit后,onStart前) */
  onRetry() {
    ps.Print.purple("mainRetry");
  }

  lifeWei: number;
  lifeWu: number;
  initData() {
    this.foodCount = GAME_CFG.foodCount;
    this.foodText.text = `${this.foodCount}`;
    const bow: TeamMate = this.teamMan.getScript(TeamMate);
    const walkMan: EnemyMate = this.enemyMan.getScript(EnemyMate);
    const teamHorse: TeamMate = this.teamHorse.getScript(TeamMate);
    const enemyHorse: EnemyMate = this.enemyHorse.getScript(EnemyMate);

    this.horseTotal = GAME_CFG.horseCount;
    this.bowTotal = GAME_CFG.bowCount;
    bow.price = GAME_CFG.bowPrice;
    bow.attackInterval = GAME_CFG.bowAttackInterval;
    bow.life = GAME_CFG.bowLife;
    teamHorse.price = GAME_CFG.horsePrice;
    teamHorse.isHorse = true;
    teamHorse.life = GAME_CFG.horseLife;
    teamHorse.attackInterval = GAME_CFG.horseAttackInterval;
    enemyHorse.isHorse = true;
    enemyHorse.attackInterval = GAME_CFG.horseAttackInterval;
    enemyHorse.life = GAME_CFG.horseLife;
    walkMan.attackInterval = GAME_CFG.walkAttackInterval;
    walkMan.life = GAME_CFG.walkLife;
    teamHorse.speed = 3;
    enemyHorse.speed = 3;
    walkMan.speed = 2;
    this.minDistance = GAME_CFG.teamGap;
    this.enemyMinDistance = GAME_CFG.enemyGap;
  }

  adjustIndex(nodes: qc.Node[]) {
    nodes = nodes.sort((a, b) => a.y - b.y);
    const beginIndex: number = nodes[0].parent.getChildIndex(nodes[0]);
    nodes.forEach((node, index) => {
      node.parent.setChildIndex(node, beginIndex + index);
    });
  }

  currentTeamRole: qc.Node;
  listenButton() {
    this.manBtn.interactive = true;
    this.horseBtn.interactive = true;
    const manGlow: qc.Node = this.manBtn.getChild("glow");
    const horseGlow: qc.Node = this.horseBtn.getChild("glow");
    this.manBtn.onDown.add(() => {
      this.currentTeamRole = this.teamMan;
      Tween.clearAll(manGlow);
      manGlow.alpha = 1;
      manGlow.visible = true;
      horseGlow.visible = false;
      this.manBtn.scaleX = this.manBtn.scaleY = 0.85;
      this.manBtn.onUp.addOnce(() => {
        this.manBtn.scaleX = this.manBtn.scaleY = 1.1;
        this.horseBtn.scaleX = this.horseBtn.scaleY = 1;
      });
    });
    this.manBtn.onDown.addOnce(() => {
      this.guide(2);
      this.spark(this.teamArea);
      this.teamArea.onDown.addOnce(() => {
        Tween.clearAll(this.teamArea);
        this.guidingDraw = false;
        this.canAttack = true;
        this.teamArea.alpha = 1;
        this.stopGuide();
        this.clearTeam();
      });
    });

    this.horseBtn.onDown.add(() => {
      if (!this.guiding2) return;
      Tween.clearAll(manGlow);
      manGlow.visible = false;
      horseGlow.visible = true;
      this.currentTeamRole = this.teamHorse;
      this.horseBtn.scaleX = this.horseBtn.scaleY = 0.85;
      this.horseBtn.onUp.addOnce(() => {
        this.horseBtn.scaleX = this.horseBtn.scaleY = 1.1;
        this.manBtn.scaleX = this.manBtn.scaleY = 1;
      });
    });

    this.fightBtn.interactive = true;
    this.fightBtn.onDown.add(() => {
      if (!this.canAttack || this.attackStarted) return;
      ps.Audio.playSound("sound_call");
      this.startAttack();
    });
  }

  disable(node: qc.Node) {
    node.scaleX = node.scaleY = 1;
    node.interactive = false;
    node.getChild("glow").visible = false;
    node.alpha = 0.5;
  }

  startAttack() {
    this.stopGuide();
    this.attackStarted = true;
    this.teamArea.getChild("drawBoard").visible = false;
    this.enemyGetInOrder();
    this.menuOut();
    this.cameraMove();
  }

  clearTeam() {
    this.team.forEach((m) => m.removeSelf());
    this.team = [];
  }

  spark(node: qc.Node) {
    node.visible = true;
    node.alpha = 1;
    Tween.to(
      node,
      {
        alpha: 0.5,
      },
      300,
      void 0,
      void 0,
      void 0,
      -1,
      true
    );
  }

  finger: ps.Finger;
  guideManLoop: qc.TimerEvent;
  guidingDraw: boolean = false;
  canAttack: boolean = false;
  attackStarted: boolean = false;
  attacking: boolean = false;
  guiding2: boolean = false;
  guide(index: number = 1) {
    if (index === 1) {
      this.finger.hide();
      this.finger.show();
      this.finger.moveToTarget0(this.manBtn);
      Tween.to(
        this.finger.gameObject,
        { scaleXY: 0.8 },
        300,
        void 0,
        void 0,
        void 0,
        -1,
        true
      );
      this.spark(this.manBtn.getChild("glow"));
    } else if (index === 2) {
      this.guiding2 = true;
      UIRoot.getChild("guideText1").visible = false;
      this.guidingDraw = true;
      this.finger.hide();
      this.finger.show();
      this.finger.startLoop(() => {
        this.clearTeam();
        this.finger
          .moveToTarget(UIRoot.getChild("guide1"), 1)
          .showImg()
          .moveToTarget(UIRoot.getChild("guide2"), 1500)
          .hideImg();
      });
      this.guidingDrawChangeLoop = this.game.timer.loop(1000, () => {
        this.guidingDraw = !this.guidingDraw;
      });
    } else if (index === 3) {
      this.finger.hide();
      this.finger.show();
      this.finger.moveToTarget0(this.fightBtn);
      Tween.to(
        this.finger.gameObject,
        { scaleXY: 0.8 },
        300,
        void 0,
        void 0,
        void 0,
        -1,
        true
      );
    }
  }
  guidingDrawChangeLoop: qc.TimerEvent;

  stopGuide() {
    if (!this.finger) return;
    this.finger.hide();
  }

  consume(delta: number) {
    this.foodCount -= delta;
    this.foodText.text = `${this.foodCount}`;
    this.foodProgress.value -= delta / GAME_CFG.foodCount;
  }

  loopDance() {
    let count: number = 13;
    const dancePoints: qc.Node[] = this.dancePoint.children;
    const that = this;
    function toDancePoints(
      dancePointsIndex: number,
      enemyMan: qc.Node,
      enemyIndex: number
    ) {
      const targetPoint: qc.Point = ps.Tools.transPos(
        dancePoints[dancePointsIndex % 4].children[enemyIndex],
        enemyMan
      );
      Tween.to(
        enemyMan,
        {
          x: targetPoint.x,
          y: targetPoint.y,
        },
        500
      ).onComplete.addOnce(() => {
        that.game.timer.add(500, () => {
          if (that.attackStarted) return;
          toDancePoints(dancePointsIndex + 1, enemyMan, enemyIndex);
        });
      });
    }
    while (count--) {
      const e: qc.Node = this.game.add.clone(this.enemyMan, this.enemyArea);
      e.visible = true;
      e.getScript(EnemyMate).dance();
      e.getScript(EnemyMate).attackInterval = GAME_CFG.walkAttackInterval;
      e.getScript(EnemyMate).life = GAME_CFG.walkLife;
      this.enemies.push(e);
      toDancePoints(0, e, count);
    }
  }

  minDistance: number = 30;
  team: qc.Node[] = [];
  onDraw(gp: qc.Point) {
    const lp: qc.Point = this.teamArea.toLocal(gp);

    if (
      lp.x > -0.5 * this.teamArea.width &&
      lp.x < 0.5 * this.teamArea.width &&
      lp.y > -0.5 * this.teamArea.height &&
      lp.y < 0.5 * this.teamArea.height &&
      this.team.every(
        (point) =>
          Mathf.getDistance(lp.x, lp.y, point.x, point.y) > this.minDistance
      )
    ) {
      this.onAddRole(lp);
    }
  }

  onAddRole(lp: qc.Point) {
    const price: number = this.currentTeamRole.getScript(TeamMate).price;

    if (!this.guidingDraw) {
      if (this.currentTeamRole === this.teamHorse) {
        if (this.horseTotal === 0 || this.foodCount < price) return;
        this.horseTotal--;
        this.horseCount.text = `X${this.horseTotal}`;
      } else {
        if (this.bowTotal === 0 || this.foodCount < price) return;
        this.bowTotal--;
        this.bowCount.text = `X${this.bowTotal}`;
      }
      this.consume(price);
    }

    const role: qc.Node = this.game.add.clone(
      this.currentTeamRole,
      this.teamArea
    );
    role.x = lp.x;
    role.y = lp.y;
    role.visible = true;
    role.getScript(TeamMate).idle();
    this.team.push(role);

    this.checkBtn();
  }

  checkBtn() {
    if (this.horseTotal === 0) {
      this.disable(this.horseBtn);
    }
    if (this.bowTotal === 0) {
      this.disable(this.manBtn);
    }
    if (
      this.foodCount < GAME_CFG.bowPrice ||
      this.foodCount < GAME_CFG.horsePrice
    ) {
      this.disable(this.horseBtn);
      this.disable(this.manBtn);
    }
    if (this.horseBtn.alpha === 0.5 && this.manBtn.alpha === 0.5) {
      this.guide(3)
    }
  }

  drawStarted: boolean = false;
  guideDraw() {
    if (this.guidingDraw && this.finger && !this.drawStarted) {
      if (this.team.length < 9) {
        this.team.forEach((m, i) => (m.alpha = i / 10));
        this.currentTeamRole = this.teamMan;
        this.onDraw(
          this.finger.gameObject.parent.toGlobal(
            new qc.Point(this.finger.gameObject.x, this.finger.gameObject.y)
          )
        );
      } else {
        this.guidingDraw = false;
      }
    }
  }

  enemyMinDistance: number = 80;
  enemyGetInOrder() {
    const startY: number = -this.enemyArea.height / 2 + 100;
    const endY: number = this.enemyArea.height / 2;
    const startX: number = 0;
    const endX: number = this.enemyArea.width / 2;
    const birthPoint: qc.Point = new qc.Point(this.enemyArea.width, 0);
    const distance: number = this.enemyMinDistance;
    let currentX: number = startX;
    let currentY: number = startY;
    let enemyIndex: number = 0;
    const speed: number = 20;
    let idleCount: number = 0;
    while (currentX < endX) {
      while (currentY < endY) {
        if (!this.enemies[enemyIndex]) {
          this.enemies[enemyIndex] = this.game.add.clone(
            this.enemyMan,
            this.enemyArea
          );
          this.enemies[enemyIndex].x = birthPoint.x;
          this.enemies[enemyIndex].y = birthPoint.y;
        }
        const e: qc.Node = this.enemies[enemyIndex];
        e.getScript(EnemyMate).walk();
        Tween.clearAll(e);
        Tween.to(
          e,
          {
            x: currentX,
            y: currentY,
          },
          Math.round(Mathf.getDistance(e.x, e.y, currentX, currentY) / speed) *
            100
        ).onComplete.addOnce(() => {
          e.getScript(EnemyMate).idle();
          idleCount++;
          if (idleCount === this.enemies.length) {
            this.enemyHorseEnter(startY, distance);
          }
        });
        currentY += distance;
        enemyIndex++;
      }
      currentX += distance;
      currentY = startY;
    }
  }

  enemyHorseEnter(startY: number, distance: number) {
    const endY: number = this.enemyArea.height / 2;
    let currentX: number = -0.25 * this.enemyArea.width;
    let currentY: number = startY + 0.5 * distance - 20;
    let idleCount: number = 0;
    let horseNum: number = 0;
    while (currentY < endY) {
      const h: qc.Node = this.game.add.clone(this.enemyHorse, this.enemyArea);
      horseNum++;
      h.visible = true;
      this.enemies.push(h);
      h.x = this.enemyArea.width;
      h.y = currentY;
      h.getScript(EnemyMate).isHorse = true;
      h.getScript(EnemyMate).walk();
      Tween.to(
        h,
        {
          x: currentX,
        },
        1000
      ).onComplete.addOnce(() => {
        h.getScript(EnemyMate).idle();
        idleCount++;
        if (idleCount === horseNum) {
          this.startFight();
        }
      });
      currentY += distance;
    }
    this.adjustIndex(this.enemyArea.children);
  }

  countLife() {
    this.progressWei.minValue = 0;
    this.progressWei.value = this.progressWei.maxValue = this.team.reduce(
      (pv, cv) => pv + cv.getScript(TeamMate).life,
      0
    );
    this.progressWu.minValue = 0;
    this.progressWu.value = this.progressWu.maxValue = this.enemies.reduce(
      (pv, cv) => pv + cv.getScript(EnemyMate).life,
      0
    );
  }

  startFight() {
    this.attacking = true;
    this.enemies.forEach((e) => {
      this.findTarget(e, this.team, EnemyMate);
      e.getScript(EnemyMate).walk();
    });
    this.team.forEach((e) => {
      this.findTarget(e, this.enemies, TeamMate);
      e.getScript(TeamMate).walk();
      e.getScript(TeamMate).startShoot();
    });
    this.countLife();
  }

  cameraMove() {
    const bg: qc.Node = UIRoot.getChild("pb_bg");
    const offsetY: number = 150;
    function move(node: qc.Node) {
      Tween.to(
        node,
        {
          y: node.y + offsetY,
        },
        1000
      );
    }
    move(bg);
    this.teamArea.children.forEach((e) => move(e));
    move(UIRoot.getChild("enemyCon"));
  }

  menuOut() {
    const menu: qc.Node = UIRoot.getChild("menu");
    const menuLayout: ps.Layout = menu.getScript(ps.Layout);
    menuLayout.enable = false;
    menu.getScript(ps.Layout);
    Tween.to(
      menu,
      {
        y: menu.y + 600,
      },
      500,
      Phaser.Easing.Back.In
    );
  }

  update() {
    this.guideDraw();
  }

  // tools
  approach(role: qc.Node, target: qc.Node, speed: number) {
    const targetPos: qc.Point = target.getWorldPosition();
    const myPos: qc.Point = role.getWorldPosition();
    const angle: number = Mathf.getAngle(
      myPos.x,
      myPos.y,
      targetPos.x,
      targetPos.y
    );
    const moveX: number = Math.cos(angle) * speed;
    const moveY: number = Math.sin(angle) * speed;
    role.x += moveX;
    role.y += moveY;
  }

  // hurt(role: qc.Node, script: TeamMate | EnemyMate, array: qc.Node[]) {
  //   const beh: TeamMate | EnemyMate = role.getScript(script);
  //   if (beh.life <= 0) return;
  //   beh.life--;
  //   if (beh.life <= 0) {
  //     beh.attackTarget = null;
  //     beh.attacking = false;
  //     array.splice(array.indexOf(role), 1);
  //     beh.die();
  //   }
  // }

  getNearest(role: qc.Node, targets: qc.Node[]) {
    let minDistance: number = Number.MAX_VALUE;
    let target: qc.Node;
    const myPos: qc.Point = role.getWorldPosition();
    targets.forEach((m) => {
      const distance: number = m.getWorldPosition().distance(myPos);
      if (distance < minDistance) {
        minDistance = distance;
        target = m;
      }
    });
    return {
      target,
      distance: minDistance,
    };
  }

  findTarget(role: qc.Node, targets: qc.Node[], script: any) {
    const { target, distance } = this.getNearest(role, targets);
    const beh: TeamMate | EnemyMate = role.getScript(script);
    beh.attackTarget = target;
  }

  getDistance(role: qc.Node, target: qc.Node) {
    return role.getWorldPosition().distance(target.getWorldPosition());
  }

  attackStart(
    role: qc.Node,
    script: any,
    enemyScript: any,
    targets: qc.Node[],
    attackTarget: qc.Node
  ) {
    const beh: TeamMate | EnemyMate = role.getScript(script);
    beh.attacking = true;
    const enemyBeh: TeamMate | EnemyMate = attackTarget.getScript(enemyScript);
    if (!enemyBeh.attacking) {
      this.attackStart(
        attackTarget,
        enemyScript,
        script,
        targets === this.team ? this.enemies : this.team,
        role
      );
    }
    beh.attack();
    if (beh.attackLoop) {
      this.game.timer.remove(beh.attackLoop);
      beh.attackLoop = null;
    }

    const that = this;
    function attack() {
      that.game.timer.add(beh.attackInterval, () => {
        const at: qc.Node = beh.attackTarget;
        if (at && beh.attacking) {
          const atBeh: TeamMate | EnemyMate = at.getScript(enemyScript);
          if (atBeh.life <= 0) {
            beh.walk();
            if (targets.length) {
              main.findTarget(role, targets, script);
            } else {
              if (targets === main.team) {
                that.game.timer.add(1000, () => {
                  ps.gameEnd();
                });
              }
              beh.attackTarget = null;
              beh.idle();
            }
            beh.attacking = false;
          } else {
            if (beh.isHorse) {
              // ps.Audio.playSound("sound_horse_attack");
              that.playAttackSound("horse");
            } else {
              that.playAttackSound("man");
              // ps.Audio.playSound("sound_man_attack");
            }
            atBeh.hurt();
            attack();
          }
        }
      });
    }
    attack();
  }

  soundCount = {
    bow: 0,
    man: 0,
    horse: 0,
    max: 10,
  };
  playAttackSound(name: string) {
    const soundName: string = `sound_${name}_attack`;
    if (this.soundCount[name] < this.soundCount.max) {
      ps.Audio.playSound(soundName);
      if (window[soundName + "_mp3"] && window[soundName + "_mp3"].sound) {
        this.soundCount[name]++;
        const duration = window[soundName + "_mp3"].sound.buffer.duration;
        setTimeout(() => {
          this.soundCount[name]--;
        }, duration);
      }
    }
  }
}
qc.registerBehaviour("MainBeh", MainBeh);
/**
帧回调（preUpdate、update、postUpdate）
如果实现了这几个函数，系统会自动每帧进行调度（当挂载的Node节点处于可见、并且本脚本的enable=true时）
初始化（awake）
如果实现了awake函数，系统会在Node节点构建完毕（反序列化完成后）自动调度
脚本可用/不可用（onEnable、onDisable）
当脚本的enable从false->true时，会自动调用onEnable函数；反之调用onDisable函数
ps:在awake结束时,如果当前脚本的enable为true，会自动调用onEnable函数
交互回调（onClick、onUp、onDown、onDrag、onDragStart、onDragEnd）
当挂载的Node具备交互时，一旦捕获相应的输入事件，这些函数会自动被调用
脚本析构（onDestroy）
当脚本被移除时，会自动调用onDestroy函数，用户可以定义必要的资源回收代码
//PlaySmart新增回调(继承ps.Behaviour)
pl状态回调(onInit、onStart、onEnding、onRetry)
如果实现了这几个函数，会在pl进行到相应状态的时候进行回调
*/
